/*
 * IMStorageWrapperM.nc -- storage operation dispatcher
 *
 * Copyright (C) Andrey Vihrov <andrey.vihrov@gmail.com>, 2010
 * Copyright (C) Askar Salimbaev <ghostinshell@gmail.com>, 2010
 *
 * This file is part of Intermote.
 *
 * Intermote is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Intermote is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Intermote.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <Intermote.h>

module IMStorageWrapperM {

	provides {
		interface IMBlockStorage as BlockStorage;

		interface IMBlockProperty as BlockProperty[uint8_t];
	}

	uses {
		interface IMBlockId as BlockId;

		interface IMBlockStorage as LocalStorage;
		interface IMBlockProperty as LocalProperty[uint8_t];

		interface IMBlockStorage as NetworkStorage;
		interface IMBlockProperty as NetworkProperty[uint8_t];
	}

}
implementation {
	enum event_id {
		EV_ALLOCATE,
		EV_READ,
		EV_WRITE,
		EV_ERASE,
	};

	uint8_t event_id;
	error_t result;

	task void signalEvents() {
		switch(event_id) {
			case EV_ALLOCATE:
				signal BlockStorage.allocateDone(result, 0); // Allocation failed
				break;
			case EV_READ:
				signal BlockStorage.writeDone(result);
				break;
			case EV_WRITE:
				signal BlockStorage.readDone(result);
				break;
			case EV_ERASE:
				signal BlockStorage.eraseDone(result);
				break;
		}
	}
	error_t signalEventOnFail(uint8_t ev, error_t status) {
		if(status != SUCCESS) {
			event_id = ev;
			result = status;
			post signalEvents();
		}
		return status;
	}
	command	error_t BlockStorage.allocate (void)
	{
		/* First try local storage */
		if (call LocalStorage.allocate() == SUCCESS)
			return SUCCESS;
		else
			return signalEventOnFail(EV_ALLOCATE, call NetworkStorage.allocate());
	}

	command error_t BlockStorage.erase (im_block_id_t block)
	{
		if (call BlockId.isLocal(block))
			return signalEventOnFail(EV_ERASE, call LocalStorage.erase(block));
		else
			return signalEventOnFail(EV_ERASE, call NetworkStorage.erase(block));
	}

	command error_t BlockStorage.read (im_block_id_t block, im_off_t offset, struct im_chunk *buf)
	{
		if (call BlockId.isLocal(block))
		{
			return signalEventOnFail(EV_READ, call LocalStorage.read(block, offset, buf));
		}
		else
			return signalEventOnFail(EV_READ, call NetworkStorage.read(block, offset, buf));
	}

	command error_t BlockStorage.write (im_block_id_t block, im_off_t offset, const struct im_chunk *buf)
	{
		if (call BlockId.isLocal(block))
			return signalEventOnFail(EV_WRITE, call LocalStorage.write(block, offset, buf));
		else
			return signalEventOnFail(EV_WRITE, call NetworkStorage.write(block, offset, buf));
	}

	command error_t BlockStorage.reserve (uint8_t count)
	{
		/* Issue the command to both components */

		call LocalStorage.reserve(count);
		call NetworkStorage.reserve(count);
			return SUCCESS;
	}


	/* BlockProperty interface operations */

	command error_t BlockProperty.set[uint8_t prop] (im_block_id_t block, uint32_t data)
	{
		if (call BlockId.isLocal(block))
			return call LocalProperty.set[prop](block, data);
		else
			return call NetworkProperty.set[prop](block, data);
	}

	command error_t BlockProperty.get[uint8_t prop] (im_block_id_t block)
	{
		if (call BlockId.isLocal(block))
			return call LocalProperty.get[prop](block);
		else
			return call NetworkProperty.get[prop](block);
	}

	default event void BlockProperty.setDone[uint8_t prop] (error_t err)
	{

	}

	default event void BlockProperty.getDone[uint8_t prop] (error_t err, uint32_t data)
	{

	}


	/* Sub-component BlockStorage and BlockProperty events */

	event void LocalStorage.allocateDone (error_t err, im_block_id_t block)
	{
		if (err == SUCCESS)
			signal BlockStorage.allocateDone(err, block);
		else
			call NetworkStorage.allocate();
	}

	event void NetworkStorage.allocateDone (error_t err, im_block_id_t block)
	{
		signal BlockStorage.allocateDone(err, block);
	}

	event void LocalStorage.readDone (error_t err)
	{
		signal BlockStorage.readDone(err);
	}

	event void NetworkStorage.readDone (error_t err)
	{
		signal BlockStorage.readDone(err);
	}

	event void LocalStorage.writeDone (error_t err)
	{
		signal BlockStorage.writeDone(err);
	}

	event void NetworkStorage.writeDone (error_t err)
	{
		signal BlockStorage.writeDone(err);
	}

		event void LocalStorage.eraseDone (error_t err)
	{
		signal BlockStorage.eraseDone(err);
	}

	event void NetworkStorage.eraseDone (error_t err)
	{
		signal BlockStorage.eraseDone(err);
	}

	event void LocalProperty.setDone[uint8_t prop] (error_t err)
	{
		signal BlockProperty.setDone[prop](err);
	}

	event void LocalProperty.getDone[uint8_t prop] (error_t err, uint32_t data)
	{
		signal BlockProperty.getDone[prop](err, data);
	}

	event void NetworkProperty.setDone[uint8_t prop] (error_t err)
	{
		signal BlockProperty.setDone[prop](err);
	}

	event void NetworkProperty.getDone[uint8_t prop] (error_t err, uint32_t data)
	{
		signal BlockProperty.getDone[prop](err, data);
	}

	default command error_t LocalProperty.set[uint8_t] (im_block_id_t, uint32_t) {
		return FAIL;
	}
	default command error_t LocalProperty.get[uint8_t] (im_block_id_t) {
		return FAIL;
	}

}
